﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Isuka.Collections;

namespace Isuka.Iteration
{
    public sealed class LinkedListIterator<T>
        : IBidirectionalIterator<T>
        , IReWritableIterator<T>
    {
        readonly LinkedList<T> container;
        LinkedListNode<T> node;

        public LinkedListIterator(LinkedList<T> list, LinkedListNode<T> node)
        {
            Contract.Requires<ArgumentNullException>(list != null, "list");
            Contract.Requires<ArgumentException>(node == null || node.List == list);

            this.container = list;
            this.node = node;
        }

        [ContractInvariantMethod]
        void ObjectInvariant()
        {
            Contract.Invariant(container != null);
        }

        bool EqualsCore(LinkedListIterator<T> other)
        {
            Contract.Requires(other != null);
            return node == other.node;
        }


        #region IBidirectionalIterator メンバー

        public void MovePrev()
        {
            if (node != null)
            {
                node = node.Previous;
            }
        }

        #endregion

        #region IIterator<TValue> メンバー

        public T Current
        {
            get
            {
                return Gate.NotNull(node).Value;
            }
            set
            {
                Gate.NotNull(node).Value = value;
            }
        }

        #endregion

        #region IIterator メンバー

        public bool Advance(int diff)
        {
            if (0 <= diff)
            {
                for (; diff != 0; diff--, MovePrev())
                    if (!InRange)
                        return false;
            }
            else
            {
                for (; diff != 0; diff++, MoveNext())
                    if (!InRange)
                        return false;
            }
            return true;
        }

        public int? Bias
        {
            get { return null; }
        }

        public object Container
        {
            get { return container; }
        }

        object IIterator.Current
        {
            get
            {
                return Current;
            }
        }

        public int? Distance(IIterator other)
        {
            var it = other as LinkedListIterator<T>;
            if (it != null && node != null && it.node != null)
            {
                var findex = container.IndexOf(node);
                var lindex = container.IndexOf(it.node);
                if (0 <= findex && 0 <= lindex)
                {
                    return lindex - findex;
                }
            }
            return null;
        }

        public bool Equals(IIterator other)
        {
            if (other == null)
            {
                return !InRange;
            }
            else
            {
                if (other is LinkedListIterator<T>)
                {
                    return EqualsCore((LinkedListIterator<T>)other);
                }
            }
            return false;
        }

        public bool InRange
        {
            get { return node != null; }
        }

        public void MoveNext()
        {
            if (node != null)
            {
                node = node.Next;
            }
        }

        #endregion

        #region ICloneable メンバー

        public object Clone()
        {
            return MemberwiseClone();
        }

        #endregion

        #region Object メンバー

        // override object.Equals
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return !InRange;
            }
            else
            {
                if (obj is LinkedListIterator<T>)
                {
                    return EqualsCore((LinkedListIterator<T>)obj);
                }
            }
            return false;
        }

        // override object.GetHashCode
        public override int GetHashCode()
        {
            // TODO: write your implementation of GetHashCode() here
            return container.GetHashCode();
        }

        #endregion

    }

}
